/*
 * hardware.c
 *
 * Created: 8/14/2013 11:15:15 AM
 *  Author: Ken Arok
 *
 * \file
 *
 * \brief Hardware Abstraction Layer.
 *
 * Copyright (c) 2013 PT Hanindo Automation Solutions. All rights reserved.
 *
 */


#include "config_board.h"

#if BOARD_1_0_USED

#include <stdint.h>
#include <stdlib.h>

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"

#include "board.h"
#include "tc.h"
#include "eic.h"
#include "intc.h"
#include "gpio.h"
#include "spi.h"
#include "twim.h"
#include "uart.h"
#include "rom.h"
#include "macaddr.h"
#include "rtc_hal.h"
#include "rom_hal.h"
#include "timer_counter.h"
#include "hardware.h"



//! Change this value if there are more than 1 external Interrupt line
#define EXT_INT_NB_LINES	1
//! Index number of Key external interrupt line
#define KEY_INT_LINE_IDX	0
//! Add the index number if exist others line


// TWI data transfer rate Hz
#define TWI_SPEED		100000

// TWI RTC Slave Address
#define TWI_RTC_SLAVE_ADDRESS			(0xA2 >> 1)	// PCF8563 Slave Address


#define TC1_CH0_IRQ				AVR32_TC1_IRQ0
#define TC1_CH1_IRQ				AVR32_TC1_IRQ1
#define TC1_CH0_IRQ_PRIORITY	AVR32_INTC_INT0
#define TC1_CH1_IRQ_PRIORITY	AVR32_INTC_INT0


//! Structure holding the configuration parameters of the EIC module
eic_options_t eic_options[EXT_INT_NB_LINES];

static volatile uint8_t _led1_hb = 0;

/** \brief Interrupt Service Routine for 10 msec timer.
 */
__attribute__((__noinline__)) static void prvTMR10ms_ISR_NonNakedBehaviour(void)
{
	uint8_t i;

	portENTER_CRITICAL();
	/* Clear  the interrupt flag. */
	tc_read_sr(TC1, 0);
	portEXIT_CRITICAL();

	/* Define code here. */
	for(i = 0; i < 32; i++) {
		if(TimersArray10ms[i]) TimersArray10ms[i]--;
	}

	for(i = 0; i < 32; i++) {
		if(FuncArray10ms[i] != NULL) FuncArray10ms[i](ParamFuncArray10ms[i]);
	}

	_led1_hb++;
	if(_led1_hb == 60) {
		gpio_set_pin_high(LED1_GPIO);
	}

	if(_led1_hb > 80) {
		gpio_set_pin_low(LED1_GPIO);
		_led1_hb = 0;
	}
}


/** \brief Interrupt Service Routine for 508 msec timer.
 */
__attribute__((__noinline__)) static void prvTMR508ms_ISR_NonNakedBehaviour(void)
{
	uint8_t i;
	
	portENTER_CRITICAL();
	/* Clear  the interrupt flag. */
	tc_read_sr(TC1, HB_CHANNEL);
	portEXIT_CRITICAL();
	
	/* Define code here. */
	for(i = 0; i < 32; i++) {
		if(TimersArray500ms[i]) TimersArray500ms[i]--;
	}

	for(i = 0; i < 32; i++) {
		if(FuncArray500ms[i] != NULL) FuncArray500ms[i](ParamFuncArray500ms[i]);
	}
}


/** \brief Interrupt Service Routine for 10 msec timer.
 */
__attribute__((__naked__)) static void prvTimer10ms(void)
{
	/* This ISR can cause a context switch, so the first statement must be a
	call to the portENTER_SWITCHING_ISR() macro.  This must be BEFORE any
	variable declarations. */
	portENTER_SWITCHING_ISR();

	prvTMR10ms_ISR_NonNakedBehaviour();

	/* Exit the ISR.  If a task was woken by either a character being received
	or transmitted then a context switch will occur. */
	portEXIT_SWITCHING_ISR();
}


/** \brief Interrupt Service Routine for 508 msec timer.
 */
__attribute__((__naked__)) static void prvTimer508ms(void)
{
	/* This ISR can cause a context switch, so the first statement must be a
	call to the portENTER_SWITCHING_ISR() macro.  This must be BEFORE any
	variable declarations. */
	portENTER_SWITCHING_ISR();

	prvTMR508ms_ISR_NonNakedBehaviour();

	/* Exit the ISR.  If a task was woken by either a character being received
	or transmitted then a context switch will occur. */
	portEXIT_SWITCHING_ISR();
}


/** \brief Initialize external interrupt.
 */
static void external_interrupt_halinit(void)
{
	/* Enable level-triggered interrupt. */
	eic_options[KEY_INT_LINE_IDX].eic_mode = EIC_MODE_EDGE_TRIGGERED;
	/* Interrupt will trigger on falling edge. */
	eic_options[KEY_INT_LINE_IDX].eic_edge = EIC_EDGE_FALLING_EDGE;
	/* Enable filter. */
	eic_options[KEY_INT_LINE_IDX].eic_filter = EIC_FILTER_ENABLED;
	/* Initialize in synchronous mode. */
	eic_options[KEY_INT_LINE_IDX].eic_async = EIC_ASYNCH_MODE;
	/* Point to External Interrupt Controller Line. */
	eic_options[KEY_INT_LINE_IDX].eic_line = EXT_INT2;
	
	/* Initialize the EIC controller with the options. */
	eic_init(&AVR32_EIC, eic_options, EXT_INT_NB_LINES);
	/* Enable Interrupt Controller Line. */
	eic_enable_line(&AVR32_EIC, eic_options[KEY_INT_LINE_IDX].eic_line);
	eic_enable_interrupt_line(&AVR32_EIC, eic_options[KEY_INT_LINE_IDX].eic_line);
	
}


/** \brief Initialize SPI0 and SPI1 module.
 */
static void spi_module_halinit(void) {
	
	spi_init();
}


/** \brief Initialize TWI0 master module.
 */
static void twims0_module_halinit(void) {
	
	twi_master_options_t twi_rtc_opt = {
		.speed = TWI_SPEED,
		.chip = TWI_RTC_SLAVE_ADDRESS,
		.smbus = false,
	};
	
	twi_master_setup(DEFAULT_TWIM0, &twi_rtc_opt);

}

/** \brief Initialize Timer/Counter1 Channel 0.
 */
static void timer_counter1_channel0_halinit(void)
{
	/* Options for waveform generation. */
	static const tc_waveform_opt_t waveform_opt = {
		/* Channel selection. */
		.channel  = 0,
		/* Software trigger effect on TIOB. */
		.bswtrg   = TC_EVT_EFFECT_NOOP,
		/* External event effect on TIOB. */
		.beevt    = TC_EVT_EFFECT_NOOP,
		/* RC compare effect on TIOB. */
		.bcpc     = TC_EVT_EFFECT_NOOP,
		/* RB compare effect on TIOB. */
		.bcpb     = TC_EVT_EFFECT_NOOP,
		/* Software trigger effect on TIOA. */
		.aswtrg   = TC_EVT_EFFECT_NOOP,
		/* External event effect on TIOA. */
		.aeevt    = TC_EVT_EFFECT_NOOP,
		/* RC compare effect on TIOA. */
		.acpc     = TC_EVT_EFFECT_NOOP,
		/* RA compare effect on TIOA.
		 * (other possibilities are none, set and clear).
		 */
		.acpa     = TC_EVT_EFFECT_NOOP,
		/* Waveform selection: Up mode with automatic trigger(reset)
		 * on RC compare.
		 */
		.wavsel   = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER,
		/* External event trigger enable. */
		.enetrg   = false,
		/* External event selection. */
		.eevt     = 0,
		/* External event edge selection. */
		.eevtedg  = TC_SEL_NO_EDGE,
		/* Counter disable when RC compare. */
		.cpcdis   = false,
		/* Counter clock stopped with RC compare. */
		.cpcstop  = false,
		/* Burst signal selection. */
		.burst    = false,
		/* Clock inversion. */
		.clki     = false,
		/* Internal source clock 3, connected to fPBA / 8. */
		.tcclks   = TC_CLOCK_SOURCE_TC3
	};

	/* Options for enabling TC interrupts. */
	static const tc_interrupt_t tc_interrupt = {
		.etrgs = 0,
		.ldrbs = 0,
		.ldras = 0,
		.cpcs  = 1,		//! Enable interrupt on RC compare alone
		.cpbs  = 0,
		.cpas  = 0,
		.lovrs = 0,
		.covfs = 0
	};

	/* Initialize the timer/counter. */
	tc_init_waveform(TC1, &waveform_opt);
	/* Generate 10 ms Timer interrupt. */
	tc_write_rc(TC1, 0, 10 * ((configPBA_CLOCK_HZ + 4 * configTICK_RATE_HZ) / (8 * configTICK_RATE_HZ)));

	/* Configure the timer interrupt. */
	tc_configure_interrupts(TC1, 0, &tc_interrupt);
	/* Register Timer/Counter1 Channel 0 Interrupt handler. */
	INTC_register_interrupt((__int_handler)&prvTimer10ms, TC1_CH0_IRQ, TC1_CH0_IRQ_PRIORITY);

	/* Start the timer/counter. */
	tc_start(TC1, 0);

}

/** \brief Initialize Timer/Counter1 Channel 1.
 */
static void timer_counter1_channel1_halinit(void)
{
	/* Options for waveform generation. */
	static const tc_waveform_opt_t waveform_opt = {
		/* Channel selection. */
		.channel  = HB_CHANNEL,
		/* Software trigger effect on TIOB. */
		.bswtrg   = TC_EVT_EFFECT_NOOP,
		/* External event effect on TIOB. */
		.beevt    = TC_EVT_EFFECT_NOOP,
		/* RC compare effect on TIOB. */
		.bcpc     = TC_EVT_EFFECT_NOOP,
		/* RB compare effect on TIOB. */
		.bcpb     = TC_EVT_EFFECT_NOOP,
		/* Software trigger effect on TIOA. */
		.aswtrg   = TC_EVT_EFFECT_NOOP,
		/* External event effect on TIOA. */
		.aeevt    = TC_EVT_EFFECT_NOOP,
		/* RC compare effect on TIOA. */
		.acpc     = TC_EVT_EFFECT_NOOP,
		/* RA compare effect on TIOA.
		 * (other possibilities are none, set and clear).
		 */
		.acpa     = TC_EVT_EFFECT_TOGGLE,
		/* Waveform selection: Up Down mode with automatic trigger(reset)
		 * on RC compare.
		 */
		.wavsel   = TC_WAVEFORM_SEL_UPDOWN_MODE_RC_TRIGGER,
		/* External event trigger enable. */
		.enetrg   = false,
		/* External event selection. */
		.eevt     = 0,
		/* External event edge selection. */
		.eevtedg  = TC_SEL_NO_EDGE,
		/* Counter disable when RC compare. */
		.cpcdis   = false,
		/* Counter clock stopped with RC compare. */
		.cpcstop  = false,
		/* Burst signal selection. */
		.burst    = false,
		/* Clock inversion. */
		.clki     = false,
		/* Internal source clock 5, connected to fPBA / 128. */
		.tcclks   = TC_CLOCK_SOURCE_TC5
	};
	
	/* Options for enabling TC interrupts. */
	static const tc_interrupt_t tc_interrupt = {
		.etrgs = 0,
		.ldrbs = 0,
		.ldras = 0,
		.cpcs  = 1,		//! Enable interrupt on RC compare alone
		.cpbs  = 0,
		.cpas  = 0,
		.lovrs = 0,
		.covfs = 0
	};
	
	/* Initialize the timer/counter. */
	tc_init_waveform(TC1, &waveform_opt);
	tc_write_rc(TC1, HB_CHANNEL, 0xFFFF);	// Generate 508 ms Timer interrupt
	tc_write_ra(TC1, HB_CHANNEL, (sysclk_get_pba_hz() / 128 / 2));
	
	/* configure the timer interrupt. */
	tc_configure_interrupts(TC1, HB_CHANNEL, &tc_interrupt);
	/* Register Timer/Counter1 Channel 1 Interrupt handler. */
	INTC_register_interrupt((__int_handler)&prvTimer508ms, TC1_CH1_IRQ, TC1_CH1_IRQ_PRIORITY);
	
	/* Start the timer/counter. */
	tc_start(TC1, HB_CHANNEL);
}


/** \brief Initialize Timer/Counter0 Channel 0.
 */
static void timer_counter0_channel0_halinit(void)
{
	/* Options for waveform generation. */
	static const tc_waveform_opt_t waveform_opt = {
		/* Channel selection. */
		.channel  = BUZZER_CHANNEL,
		/* Software trigger effect on TIOB. */
		.bswtrg   = TC_EVT_EFFECT_NOOP,
		/* External event effect on TIOB. */
		.beevt    = TC_EVT_EFFECT_NOOP,
		/* RC compare effect on TIOB. */
		.bcpc     = TC_EVT_EFFECT_CLEAR,
		/* RB compare effect on TIOB. */
		.bcpb     = TC_EVT_EFFECT_SET,
		/* Software trigger effect on TIOA. */
		.aswtrg   = TC_EVT_EFFECT_NOOP,
		/* External event effect on TIOA. */
		.aeevt    = TC_EVT_EFFECT_NOOP,
		/* RC compare effect on TIOA. */
		.acpc     = TC_EVT_EFFECT_NOOP,
		/* RA compare effect on TIOA.
		 * (other possibilities are none, set and clear).
		 */
		.acpa     = TC_EVT_EFFECT_NOOP,
		/* Waveform selection: Up mode with automatic trigger(reset)
		 * on RC compare.
		 */
		.wavsel   = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER,
		/* External event trigger enable. */
		.enetrg   = false,
		/* External event selection. */
		.eevt     = 1,
		/* External event edge selection. */
		.eevtedg  = TC_SEL_NO_EDGE,
		/* Counter disable when RC compare. */
		.cpcdis   = false,
		/* Counter clock stopped with RC compare. */
		.cpcstop  = false,
		/* Burst signal selection. */
		.burst    = false,
		/* Clock inversion. */
		.clki     = false,
		/* Internal source clock 2, connected to fPBC / 2. */
		.tcclks   = TC_CLOCK_SOURCE_TC2
	};
	
	/* Initialize the timer/counter. */
	tc_init_waveform(TC0, &waveform_opt);
	tc_write_rc(TC0, BUZZER_CHANNEL, 4620);
	tc_write_rb(TC0, BUZZER_CHANNEL, 1980);
		
	/* Start the timer/counter. */
	//tc_start(TC0, BUZZER_CHANNEL);
}


/** \brief Initialize Timer/Counter0 Channel 2.
 */
static void timer_counter0_channel2_halinit(void)
{
	/* Options for waveform generation. */
	static const tc_waveform_opt_t waveform_opt = {
		/* Channel selection. */
		.channel  = TFT_BLON_CHANNEL,
		/* Software trigger effect on TIOB. */
		.bswtrg   = TC_EVT_EFFECT_NOOP,
		/* External event effect on TIOB. */
		.beevt    = TC_EVT_EFFECT_NOOP,
		/* RC compare effect on TIOB. */
		.bcpc     = TC_EVT_EFFECT_NOOP,
		/* RB compare effect on TIOB. */
		.bcpb     = TC_EVT_EFFECT_NOOP,
		/* Software trigger effect on TIOA. */
		.aswtrg   = TC_EVT_EFFECT_NOOP,
		/* External event effect on TIOA. */
		.aeevt    = TC_EVT_EFFECT_NOOP,
		/* RC compare effect on TIOA. */
		.acpc     = TC_EVT_EFFECT_TOGGLE,
		/* RA compare effect on TIOA.
		 * (other possibilities are none, set and clear).
		 */
		.acpa     = TC_EVT_EFFECT_NOOP,
		/* Waveform selection: Up Down mode with automatic trigger(reset)
		 * on RC compare.
		 */
		.wavsel   = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER,
		/* External event trigger enable. */
		.enetrg   = false,
		/* External event selection. */
		.eevt     = 0,
		// External event edge selection.
		.eevtedg  = TC_SEL_NO_EDGE,
		/* Counter disable when RC compare. */
		.cpcdis   = false,
		/* Counter clock stopped with RC compare. */
		.cpcstop  = false,
		/* Burst signal selection. */
		.burst    = false,
		/* Clock inversion. */
		.clki     = false,
		/* Internal source clock 2, connected to fPBC / 2. */
		.tcclks   = TC_CLOCK_SOURCE_TC2
	};
	
	/* Initialize the timer/counter. */
	tc_init_waveform(TC0, &waveform_opt);
	tc_write_rc(TC0, TFT_BLON_CHANNEL, 0xFFFF);
	tc_write_ra(TC0, TFT_BLON_CHANNEL, (sysclk_get_pbc_hz() / 128 / 2));
	
	/* Start the timer/counter. */
	tc_start(TC0, TFT_BLON_CHANNEL);	
	
}

/* \brief Check SDRAM hardware.
 *
 * \return	0x00000000 = OK; 0x00000001 = Error.
 *
 */
static uint32_t hardware_sdram_check(void)
{
	uint8_t *_ptr;
	
	_ptr = malloc(TOTAL_HEAP_SIZE - 64 * 1024);
	if(_ptr == NULL) return 0x00000001;
	
	free(_ptr);
	return 0x00000000;
}


/* \brief Check ROM hardware.
 *
 * \return	0x00000000 = OK; 0x00000002 = Error.
 *
 */
static uint32_t hardware_rom_check(void)
{
	uint32_t _rom_id;
	
	/* Read ROM ID. */
	_rom_id = rom_read_id();
	
	if(_rom_id == ROM_ID) {
		return 0x00000000;
	}
	else {
		return 0x00000002;
	}
}

/* \brief Check MAC Address hardware.
 *
 * \return	0x00000000 = OK; 0x00000004 = Error.
 *
 */
static uint32_t hardware_mac_addr_check(void)
{
	uint32_t _tmp32 = 0x00000000;
	uint8_t _dummy_MAC[6];
	
	macaddr_read((void *)_dummy_MAC);
	_tmp32 = _dummy_MAC[0];
	_tmp32 = _tmp32 << 8;
	_tmp32 |= _dummy_MAC[1];
	_tmp32 =  _tmp32 << 8;
	_tmp32 |= _dummy_MAC[2];
	
	if(_tmp32 == MAC_OUI) {
		return 0x00000000;
	}
	else {
		return 0x00000004;
	}
}

/* \brief Check RTC hardware.
 *
 * \return	0x00000000 = OK; 0x00000004 = Error.
 *
 */
static uint32_t hardware_rtc_check(void)
{
	uint8_t _rtc_code;
	
	_rtc_code = rtc_read_ctrl();
	if(_rtc_code == TIMER_CTRL_REG_DEF) {
		return 0x00000000;
	}
	else {
		return 0x00000008;
	}
}


/* ------------------------------------------------------------------------------------------------ */

void hardware_init(void)
{
	/* Configure board I/O. */
	board_init();
	
	/* Initialize External Interrupt module */
	external_interrupt_halinit();
	
	/* Initialize SPIO and SPI1 module */
	spi_module_halinit();
	
	/* Initialize TWI0 module */
	twims0_module_halinit();
	
	/* Initialize Timer/Counter1 Channel 0 */
	timer_counter1_channel0_halinit();

	/* Initialize Timer/Counter1 Channel 1 */
	timer_counter1_channel1_halinit();
	
	/* Initialize Timer/Counter0 Channel 0 */
	timer_counter0_channel0_halinit();
	
	/* Initialize Timer/Counter0 Channel 2 */
	timer_counter0_channel2_halinit();
	
	/* Initialize ROM. */
	rom_init();
	
	Enable_global_interrupt();
	
}


uint32_t hardware_check(void)
{
	uint32_t _retcode = 0x00000000, _tmp32;
	
	/* Check SDRAM. */
	_tmp32 = hardware_sdram_check();
	_retcode |= _tmp32;
	
	/* Check ROM. */
	_tmp32 = hardware_rom_check();
	_retcode |= _tmp32;
	
	/* Check MAC Address. */
	_tmp32 = hardware_mac_addr_check();
	_retcode |= _tmp32;
	
	/* Check RTC. */
	_tmp32 = hardware_rtc_check();
	_retcode |= _tmp32;
	
	return _retcode;
}

#endif /* BOARD_1_0_USED */

